#include "./PORT/port.h"

void (*gpio_ex_callback_funs[16])(void);

#ifndef NULL
#define NULL (void *)0
#endif

typedef struct
{
    GPIO_TypeDef *port;
    uint16_t pin;
} gpio_config_t;

// clang-format off
/* 引脚映射表 */
const gpio_config_t gpio_map[] =
{
    {GPIOA, GPIO_PIN_0},{GPIOA, GPIO_PIN_1},{GPIOA, GPIO_PIN_2},{GPIOA, GPIO_PIN_3}, {GPIOA, GPIO_PIN_4},{GPIOA, GPIO_PIN_5},{GPIOA, GPIO_PIN_6},{GPIOA, GPIO_PIN_7},
    {GPIOA, GPIO_PIN_8},{GPIOA, GPIO_PIN_9},{GPIOA, GPIO_PIN_10},{GPIOA, GPIO_PIN_11},{GPIOA, GPIO_PIN_12},{GPIOA, GPIO_PIN_13},{GPIOA, GPIO_PIN_14},{GPIOA, GPIO_PIN_15},
    {GPIOB, GPIO_PIN_0},{GPIOB, GPIO_PIN_1},{GPIOB, GPIO_PIN_2},{GPIOB, GPIO_PIN_3},{GPIOB, GPIO_PIN_4},{GPIOB, GPIO_PIN_5},{GPIOB, GPIO_PIN_6},{GPIOB, GPIO_PIN_7},
    {GPIOB, GPIO_PIN_8},{GPIOB, GPIO_PIN_9},{GPIOB, GPIO_PIN_10},{GPIOB, GPIO_PIN_11},{GPIOB, GPIO_PIN_12},{GPIOB, GPIO_PIN_13},{GPIOB, GPIO_PIN_14},{GPIOB, GPIO_PIN_15},
    {GPIOC, GPIO_PIN_0},{GPIOC, GPIO_PIN_1},{GPIOC, GPIO_PIN_2},{GPIOC, GPIO_PIN_3},{GPIOC, GPIO_PIN_4},{GPIOC, GPIO_PIN_5},{GPIOC, GPIO_PIN_6},{GPIOC, GPIO_PIN_7},
    {GPIOC, GPIO_PIN_8},{GPIOC, GPIO_PIN_9},{GPIOC, GPIO_PIN_10},{GPIOC, GPIO_PIN_11},{GPIOC, GPIO_PIN_12},{GPIOC, GPIO_PIN_13},{GPIOC, GPIO_PIN_14},{GPIOC, GPIO_PIN_15},
    {GPIOD, GPIO_PIN_0},{GPIOD, GPIO_PIN_1},{GPIOD, GPIO_PIN_2},{GPIOD, GPIO_PIN_3},{GPIOD, GPIO_PIN_4},{GPIOD, GPIO_PIN_5},{GPIOD, GPIO_PIN_6},{GPIOD, GPIO_PIN_7},
    {GPIOD, GPIO_PIN_8},{GPIOD, GPIO_PIN_9},{GPIOD, GPIO_PIN_10},{GPIOD, GPIO_PIN_11},{GPIOD, GPIO_PIN_12},{GPIOD, GPIO_PIN_13},{GPIOD, GPIO_PIN_14},{GPIOD, GPIO_PIN_15},
    {GPIOE, GPIO_PIN_0},{GPIOE, GPIO_PIN_1},{GPIOE, GPIO_PIN_2},{GPIOE, GPIO_PIN_3},{GPIOE, GPIO_PIN_4},{GPIOE, GPIO_PIN_5},{GPIOE, GPIO_PIN_6},{GPIOE, GPIO_PIN_7},
    {GPIOE, GPIO_PIN_8},{GPIOE, GPIO_PIN_9},{GPIOE, GPIO_PIN_10},{GPIOE, GPIO_PIN_11},{GPIOE, GPIO_PIN_12},{GPIOE, GPIO_PIN_13},{GPIOE, GPIO_PIN_14},{GPIOE, GPIO_PIN_15},
    {GPIOF, GPIO_PIN_0},{GPIOF, GPIO_PIN_1},{GPIOF, GPIO_PIN_2},{GPIOF, GPIO_PIN_3},{GPIOF, GPIO_PIN_4},{GPIOF, GPIO_PIN_5},{GPIOF, GPIO_PIN_6},{GPIOF, GPIO_PIN_7},
    {GPIOF, GPIO_PIN_8},{GPIOF, GPIO_PIN_9},{GPIOF, GPIO_PIN_10},{GPIOF, GPIO_PIN_11},{GPIOF, GPIO_PIN_12},{GPIOF, GPIO_PIN_13},{GPIOF, GPIO_PIN_14},{GPIOF, GPIO_PIN_15},
    {GPIOG, GPIO_PIN_0},{GPIOG, GPIO_PIN_1},{GPIOG, GPIO_PIN_2},{GPIOG, GPIO_PIN_3},{GPIOG, GPIO_PIN_4},{GPIOG, GPIO_PIN_5},{GPIOG, GPIO_PIN_6},{GPIOG, GPIO_PIN_7},
    {GPIOG, GPIO_PIN_8},{GPIOG, GPIO_PIN_9},{GPIOG, GPIO_PIN_10},{GPIOG, GPIO_PIN_11},{GPIOG, GPIO_PIN_12},{GPIOG, GPIO_PIN_13},{GPIOG, GPIO_PIN_14},{GPIOG, GPIO_PIN_15},
    {GPIOH, GPIO_PIN_0},{GPIOH, GPIO_PIN_1},{GPIOH, GPIO_PIN_2},{GPIOH, GPIO_PIN_3},{GPIOH, GPIO_PIN_4},{GPIOH, GPIO_PIN_5},{GPIOH, GPIO_PIN_6},{GPIOH, GPIO_PIN_7},
    {GPIOH, GPIO_PIN_8},{GPIOH, GPIO_PIN_9},{GPIOH, GPIO_PIN_10},{GPIOH, GPIO_PIN_11},{GPIOH, GPIO_PIN_12},{GPIOH, GPIO_PIN_13},{GPIOH, GPIO_PIN_14},{GPIOH, GPIO_PIN_15},
    {GPIOI, GPIO_PIN_0},{GPIOI, GPIO_PIN_1},{GPIOI, GPIO_PIN_2},{GPIOI, GPIO_PIN_3},{GPIOI, GPIO_PIN_4},{GPIOI, GPIO_PIN_5},{GPIOI, GPIO_PIN_6},{GPIOI, GPIO_PIN_7},
    {GPIOI, GPIO_PIN_8},{GPIOI, GPIO_PIN_9},{GPIOI, GPIO_PIN_10},{GPIOI, GPIO_PIN_11},{GPIOI, GPIO_PIN_12},{GPIOI, GPIO_PIN_13},{GPIOI, GPIO_PIN_14},{GPIOI, GPIO_PIN_15},
};
// clang-format on

/**
 * @brief       外部中断配置函数, 只针对GPIOA~GPIOI
 * @note        该函数会自动开启对应中断, 以及屏蔽线
 * @param       pinx:PA0~PI15, GPIO引脚位置
 * @param       tmode: 1~3, 触发模式
 *   @arg       GPIO_FTIR, 1, 下降沿触发
 *   @arg       GPIO_RTIR, 2, 上升沿触发
 *   @arg       GPIO_BTIR, 3, 任意电平触发
 * @param       callback: 外部中断回调函数, 当GPIO引脚发生中断时会调用此函数
 * @retval      无
 */
void gpio_nvic_ex_config(uint8_t pinx, uint8_t tmode, void (*callback)(void))
{
    uint8_t offset;
    uint32_t gpio_num = 0; /* gpio编号, 0~10, 代表GPIOA~GPIOG */
    uint32_t pinpos = 0, pos = 0, curpin = 0;

    gpio_num = ((uint32_t)gpio_map[pinx].port - (uint32_t)GPIOA) / 0X400; /* 得到gpio编号 */
    RCC->APB2ENR |= 1 << 14;                                              /* 使能SYSCFG时钟  */

    for (pinpos = 0; pinpos < 16; pinpos++)
    {
        pos = 1 << pinpos;                 /* 一个个位检查 */
        curpin = gpio_map[pinx].pin & pos; /* 检查引脚是否要设置 */

        if (curpin == pos) /* 需要设置 */
        {
            offset = (pinpos % 4) * 4;
            SYSCFG->EXTICR[pinpos / 4] &= ~(0x000F << offset); /* 清除原来设置！！！ */
            SYSCFG->EXTICR[pinpos / 4] |= gpio_num << offset;  /* EXTI.BITx映射到gpiox.bitx */

            EXTI->IMR |= 1 << pinpos; /* 开启line BITx上的中断(如果要禁止中断，则反操作即可) */

            if (tmode & 0x01)
                EXTI->FTSR |= 1 << pinpos; /* line bitx上事件下降沿触发 */

            if (tmode & 0x02)
                EXTI->RTSR |= 1 << pinpos; /* line bitx上事件上升降沿触发 */
        }
    }
    gpio_pin_init(pinx, GPIO_MODE_IN, GPIO_OTYPE_PP, GPIO_SPEED_LOW, GPIO_PUPD_PU); /* 设置为输入模式 */
    gpio_ex_callback_register(pinx, callback);                                      /* 注册外部中断回调函数 */
}

/**
 * @brief       GPIO外部中断回调函数注册
 * @param       pinx:PA0~PI15, GPIO引脚位置
 * @param       callback: 回调函数指针, 当GPIO引脚发生中断时会调用此函数
 * @retval      无
 */
void gpio_ex_callback_register(uint8_t pinx, void (*callback)(void))
{
    uint8_t iqrn = 0;
    iqrn = pinx % 16; /* 得到gpio引脚位置, 0~15 */

    gpio_ex_callback_funs[iqrn] = callback; /* 设置回调函数 */

    if (iqrn == 0)
        iqrn = EXTI0_IRQn;
    else if (iqrn == 1)
        iqrn = EXTI1_IRQn;
    else if (iqrn == 2)
        iqrn = EXTI2_IRQn;
    else if (iqrn == 3)
        iqrn = EXTI3_IRQn;
    else if (iqrn == 4)
        iqrn = EXTI4_IRQn;
    else if (iqrn < 10)
        iqrn = EXTI9_5_IRQn; /* 5~9 */
    else
        iqrn = EXTI15_10_IRQn; /* 10~15 */

    sys_nvic_init(3, 3, iqrn, 2); /* 组2，最低优先级 */
}

/**
 * @brief       GPIO外部中断回调函数注销
 * @param       pinx:PA0~PI15, GPIO引脚位置
 * @retval      无
 */
void gpio_ex_callback_deregister(uint8_t pinx)
{
    pinx %= 16;                         /* 得到gpio引脚位置, 0~15 */
    gpio_ex_callback_funs[pinx] = NULL; /* 清除回调函数 */
}

/**
 * @brief       GPIO复用功能选择设置
 * @param       gpio_map[pinx].port: GPIOA~GPIOI, GPIO指针
 * @param       pinx: 0X0000~0XFFFF, 引脚位置, 每个位代表一个IO, 第0位代表Px0, 第1位代表Px1, 依次类推. 比如0X0101, 代表同时设置Px0和Px8.
 *   @arg       SYS_GPIO_PIN0~SYS_GPIO_PIN15, 1<<0 ~ 1<<15
 * @param       afx:0~15, 代表AF0~AF15.
 *              AF0~15设置情况(这里仅是列出常用的, 详细的请见STM32F407xx数据手册, Table 7):
 *   @arg       AF0:MCO/SWD/SWCLK/RTC       AF1:TIM1/TIM2               AF2:TIM3~5                  AF3:TIM8~11
 *   @arg       AF4:I2C1~I2C3               AF5:SPI1/SPI2/I2S2          AF6:SPI3/I2S3               AF7:USART1~3
 *   @arg       AF8:USART4~6                AF9;CAN1/CAN2/TIM12~14      AF10:USB_OTG/USB_HS         AF11:ETH
 *   @arg       AF12:FSMC/SDIO/OTG_FS       AF13:DCIM                   AF14:                       AF15:EVENTOUT
 * @retval      无
 */
void gpio_af_set(uint8_t pinx, uint8_t afx)
{
    uint32_t pinpos = 0, pos = 0, curpin = 0;
    ;

    for (pinpos = 0; pinpos < 16; pinpos++)
    {
        pos = 1 << pinpos;                 /* 一个个位检查 */
        curpin = gpio_map[pinx].pin & pos; /* 检查引脚是否要设置 */

        if (curpin == pos) /* 需要设置 */
        {
            gpio_map[pinx].port->AFR[pinpos >> 3] &= ~(0X0F << ((pinpos & 0X07) * 4));
            gpio_map[pinx].port->AFR[pinpos >> 3] |= (uint32_t)afx << ((pinpos & 0X07) * 4);
        }
    }
}

/**
 * @brief       GPIO通用设置
 * @param       pinx:PA0~PI15, GPIO引脚位置
 *
 * @param       mode: 0~3; 模式选择, 设置如下:
 *   @arg       GPIO_MODE_IN,  0, 输入模式(系统复位默认状态)
 *   @arg       GPIO_MODE_OUT, 1, 输出模式
 *   @arg       GPIO_MODE_AF,  2, 复用功能模式
 *   @arg       GPIO_MODE_AIN, 3, 模拟输入模式
 *
 * @param       otype: 0 / 1; 输出类型选择, 设置如下:
 *   @arg       GPIO_OTYPE_PP, 0, 推挽输出
 *   @arg       GPIO_OTYPE_OD, 1, 开漏输出
 *
 * @param       ospeed: 0~2; 输出速度, 设置如下(注意: 不能为0!!):
 *   @arg       GPIO_SPEED_LOW,         0, 低速
 *   @arg       GPIO_SPEED_MID,         1, 中速
 *   @arg       GPIO_SPEED_HIGH,        2, 高速
 *   @arg       GPIO_SPEED_VERY_HIGH,   3, 超高速
 *
 * @param       pupd: 0~3: 上下拉设置, 设置如下:
 *   @arg       GPIO_PUPD_NONE, 0, 不带上下拉
 *   @arg       GPIO_PUPD_PU,   1, 上拉
 *   @arg       GPIO_PUPD_PD,   2, 下拉
 *   @arg       GPIO_PUPD_RES,  3, 保留
 *
 * @note:       注意: 在输入模式(普通输入/模拟输入)下, OTYPE和OSPEED参数无效!!
 * @retval      无
 */
void gpio_pin_init(uint8_t pinx, uint32_t mode, uint32_t otype, uint32_t ospeed, uint32_t pupd)
{
    uint32_t pinpos = 0, pos = 0, curpin = 0;

    if (gpio_map[pinx].port == GPIOA)
        RCC->AHB1ENR |= 1 << 0; /* GPIOA = 1,使能GPIOA时钟 */
    else if (gpio_map[pinx].port == GPIOB)
        RCC->AHB1ENR |= 1 << 1; /* GPIOB = 1,使能GPIOB时钟 */
    else if (gpio_map[pinx].port == GPIOC)
        RCC->AHB1ENR |= 1 << 2; /* GPIOC = 1,使能GPIOC时钟 */
    else if (gpio_map[pinx].port == GPIOD)
        RCC->AHB1ENR |= 1 << 3; /* GPIOD = 1,使能GPIOD时钟 */
    else if (gpio_map[pinx].port == GPIOE)
        RCC->AHB1ENR |= 1 << 4; /* GPIOE = 1,使能GPIOE时钟 */
    else if (gpio_map[pinx].port == GPIOF)
        RCC->AHB1ENR |= 1 << 5; /* GPIOF = 1,使能GPIOF时钟 */
    else if (gpio_map[pinx].port == GPIOG)
        RCC->AHB1ENR |= 1 << 6; /* GPIOG = 1,使能GPIOG时钟 */
    else if (gpio_map[pinx].port == GPIOH)
        RCC->AHB1ENR |= 1 << 7; /* GPIOH = 1,使能GPIOH时钟 */
    else if (gpio_map[pinx].port == GPIOI)
        RCC->AHB1ENR |= 1 << 8; /* GPIOI = 1,使能GPIOI时钟 */

    for (pinpos = 0; pinpos < 16; pinpos++)
    {
        pos = 1 << pinpos;                 /* 一个个位检查 */
        curpin = gpio_map[pinx].pin & pos; /* 检查引脚是否要设置 */

        if (curpin == pos) /* 需要设置 */
        {
            gpio_map[pinx].port->MODER &= ~(3 << (pinpos * 2)); /* 先清除原来的设置 */
            gpio_map[pinx].port->MODER |= mode << (pinpos * 2); /* 设置新的模式 */

            if ((mode == 0X01) || (mode == 0X02)) /* 如果是输出模式/复用功能模式 */
            {
                gpio_map[pinx].port->OSPEEDR &= ~(3 << (pinpos * 2));     /* 清除原来的设置 */
                gpio_map[pinx].port->OSPEEDR |= (ospeed << (pinpos * 2)); /* 设置新的速度值 */
                gpio_map[pinx].port->OTYPER &= ~(1 << pinpos);            /* 清除原来的设置 */
                gpio_map[pinx].port->OTYPER |= otype << pinpos;           /* 设置新的输出模式 */
            }

            gpio_map[pinx].port->PUPDR &= ~(3 << (pinpos * 2)); /* 先清除原来的设置 */
            gpio_map[pinx].port->PUPDR |= pupd << (pinpos * 2); /* 设置新的上下拉 */
        }
    }
}

/**
 * @brief       设置GPIO某个引脚的输出状态
 * @param        pinx:PA0~PG15, GPIO引脚位置
 * @param       status: 0/1, 引脚状态(仅最低位有效), 设置如下:
 *   @arg       0, 输出低电平
 *   @arg       1, 输出高电平
 * @retval      无
 */
void gpio_write_pin(uint8_t pinx, uint8_t status)
{
    if (status & 0X01)
    {
        gpio_map[pinx].port->BSRR |= gpio_map[pinx].pin; /* 设置GPIOx的pinx为1 */
    }
    else
    {
        gpio_map[pinx].port->BSRR |= (uint32_t)gpio_map[pinx].pin << 16; /* 设置GPIOx的pinx为0 */
    }
}

/**
 * @brief       读取GPIO某个引脚的状态
 * @param        pinx:PA0~PG15, GPIO引脚位置
 * @retval      返回引脚状态, 0, 低电平; 1, 高电平
 */
uint8_t gpio_read_pin(uint8_t pinx)
{
    if (gpio_map[pinx].port->IDR & gpio_map[pinx].pin)
    {
        return 1; /* pinx的状态为1 */
    }
    else
    {
        return 0; /* pinx的状态为0 */
    }
}

/**
 * @brief       切换GPIO某个引脚的状态
 * @param        pinx:PA0~PG15, GPIO引脚位置
 * @retval      无
 */
void gpio_toggle_pin(uint8_t pinx)
{
    gpio_map[pinx].port->ODR ^= gpio_map[pinx].pin;
}

void EXTI0_IRQHandler(void)
{
    if (EXTI->PR & (1 << 0)) /* 检查是否是line 0 */
    {
        EXTI->PR |= (1 << 0); /* 清除line 0的中断标志 */
        if (gpio_ex_callback_funs[0] != NULL)
            gpio_ex_callback_funs[0](); /* 调用回调函数 */
    }
}

void EXTI1_IRQHandler(void)
{
    if (EXTI->PR & (1 << 1)) /* 检查是否是line 1 */
    {
        EXTI->PR |= (1 << 1); /* 清除line 1的中断标志 */
        if (gpio_ex_callback_funs[1] != NULL)
            gpio_ex_callback_funs[1](); /* 调用回调函数 */
    }
}

void EXTI2_IRQHandler(void)
{
    if (EXTI->PR & (1 << 2)) /* 检查是否是line 2 */
    {
        EXTI->PR |= (1 << 2); /* 清除line 2的中断标志 */
        if (gpio_ex_callback_funs[2] != NULL)
            gpio_ex_callback_funs[2](); /* 调用回调函数 */
    }
}

void EXTI3_IRQHandler(void)
{
    if (EXTI->PR & (1 << 3)) /* 检查是否是line 3 */
    {
        EXTI->PR |= (1 << 3); /* 清除line 3的中断标志 */
        if (gpio_ex_callback_funs[3] != NULL)
            gpio_ex_callback_funs[3](); /* 调用回调函数 */
    }
}

void EXTI4_IRQHandler(void)
{
    if (EXTI->PR & (1 << 4)) /* 检查是否是line 4 */
    {
        EXTI->PR |= (1 << 4); /* 清除line 4的中断标志 */
        if (gpio_ex_callback_funs[4] != NULL)
            gpio_ex_callback_funs[4](); /* 调用回调函数 */
    }
}

void EXTI9_5_IRQHandler(void)
{
    if (EXTI->PR & (1 << 5)) /* 检查是否是line 5 */
    {
        EXTI->PR |= (1 << 5); /* 清除line 5的中断标志 */
        if (gpio_ex_callback_funs[5] != NULL)
            gpio_ex_callback_funs[5](); /* 调用回调函数 */
    }
    if (EXTI->PR & (1 << 6)) /* 检查是否是line 6 */
    {
        EXTI->PR |= (1 << 6); /* 清除line 6的中断标志 */
        if (gpio_ex_callback_funs[6] != NULL)
            gpio_ex_callback_funs[6](); /* 调用回调函数 */
    }
    if (EXTI->PR & (1 << 7)) /* 检查是否是line 7 */
    {
        EXTI->PR |= (1 << 7); /* 清除line 7的中断标志 */
        if (gpio_ex_callback_funs[7] != NULL)
            gpio_ex_callback_funs[7](); /* 调用回调函数 */
    }
    if (EXTI->PR & (1 << 8)) /* 检查是否是line 8 */
    {
        EXTI->PR |= (1 << 8); /* 清除line 8的中断标志 */
        if (gpio_ex_callback_funs[8] != NULL)
            gpio_ex_callback_funs[8](); /* 调用回调函数 */
    }
    if (EXTI->PR & (1 << 9)) /* 检查是否是line 9 */
    {
        EXTI->PR |= (1 << 9); /* 清除line 9的中断标志 */
        if (gpio_ex_callback_funs[9] != NULL)
            gpio_ex_callback_funs[9](); /* 调用回调函数 */
    }
}

void EXTI15_10_IRQHandler(void)
{
    if (EXTI->PR & (1 << 10)) /* 检查是否是line 10 */
    {
        EXTI->PR |= (1 << 10); /* 清除line 10的中断标志 */
        if (gpio_ex_callback_funs[10] != NULL)
            gpio_ex_callback_funs[10](); /* 调用回调函数 */
    }
    if (EXTI->PR & (1 << 11)) /* 检查是否是line 11 */
    {
        EXTI->PR |= (1 << 11); /* 清除line 11的中断标志 */
        if (gpio_ex_callback_funs[11] != NULL)
            gpio_ex_callback_funs[11](); /* 调用回调函数 */
    }
    if (EXTI->PR & (1 << 12)) /* 检查是否是line 12 */
    {
        EXTI->PR |= (1 << 12); /* 清除line 12的中断标志 */
        if (gpio_ex_callback_funs[12] != NULL)
            gpio_ex_callback_funs[12](); /* 调用回调函数 */
    }
    if (EXTI->PR & (1 << 13)) /* 检查是否是line 13 */
    {
        EXTI->PR |= (1 << 13); /* 清除line 13的中断标志 */
        if (gpio_ex_callback_funs[13] != NULL)
            gpio_ex_callback_funs[13](); /* 调用回调函数 */
    }
    if (EXTI->PR & (1 << 14)) /* 检查是否是line 14 */
    {
        EXTI->PR |= (1 << 14); /* 清除line 14的中断标志 */
        if (gpio_ex_callback_funs[14] != NULL)
            gpio_ex_callback_funs[14](); /* 调用回调函数 */
    }
    if (EXTI->PR & (1 << 15)) /* 检查是否是line 15 */
    {
        EXTI->PR |= (1 << 15); /* 清除line 15的中断标志 */
        if (gpio_ex_callback_funs[15] != NULL)
            gpio_ex_callback_funs[15](); /* 调用回调函数 */
    }
}
